#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import socket
import thread
import time
import re
import sys
import os
import imp

from util import ereg
from libuserchan import *
import ircparser

import config

sys.path.append('./modules')

author = "hukk"
name = "Pyrc"
version = "0.1.9"

def send_version(pyrc,umask,dest,message):
    pyrc.notice(umask[0],"VERSION %s %s by %s"% (name,version,author))

def send_finger(pyrc,umask,dest,message):
    pyrc.notice(umask[0],"FINGER %s %s by %s"% (name,version,author))
    
def send_time(pyrc,umask,dest,message):
    pyrc.notice(umask[0],"TIME %s" % time.time())
    
def doreload(pyrc,umask,dest,message):
    pyrc.reloadconfig()
    pyrc.msg(dest[0],"Reloaded (%d modules, %d events)" % (len(pyrc.loaded_modules),len(pyrc.events)))
    
def show_info(pyrc,umask,dest,message):
    pyrc.msg(dest[0],"*** Showing Pyrc Info ***")
    pyrc.msg(dest[0],"Version: %s %s by %s" % (name,version,author))
    pyrc.msg(dest[0],"Modules: %d modules, %d events" % (len(pyrc.loaded_modules),len(pyrc.events)))
    pyrc.msg(dest[0],"Platform: %s" % os.sys.platform)
    
class Pyrc:
    
    re_nick = "([a-zA-Z\[\]\{\}\-_^`][a-zA-Z\[\]\{\}\-_^`0-9]*)"
    re_user = "(~?[a-zA-Z0-9=]+)"
    re_host = "([^ @!]+)"
    re_chan = "(#[^, ]+)"
    re_dest = "(%s|%s)" % (re_nick,re_chan)
    re_umask = "(%s!%s@%s)" % (re_nick,re_user,re_host)
    
    re_PING     = "PING \:(.+)"
    re_PRIVMSG  = ":%s PRIVMSG %s :(.*)"                %(re_umask,re_dest)
    re_NOTICE   = ":%s NOTICE %s :(.*)"                 %(re_umask,re_dest)
    re_JOIN     = ":%s JOIN :(%s)(:%s .*)?"              %(re_umask,re_chan,re_host)
    re_PART     = ":%s PART %s :(.*)"                   %(re_umask,re_chan)
    re_QUIT     = ":%s QUIT :(.*)"                      %(re_umask)
    re_MODE     = ":%s MODE %s (\-|\+)([a-zA-Z]{1}) (.*)" %(re_umask,re_dest)
    re_NICK     = ":%s NICK :%s"                %(re_umask,re_nick)
    re_ERRO     = "ERROR :Closing Link:.+"
    re_RAW      = "(:[^ ]+ )?([0-9]{3}) ([^ ]+) (.+)" 
    
    
    def __init__(self):
   
        self.host = config.server
        self.port = config.port
        self.me = config.nick
        self.user = "%s \"\" \"%s\" :%s" % (config.user,config.server, config.realname)
        self.chan = []
        self.loaded_modules = []
        self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.buf = 4096
        self.connected = False
        self.closed = False
        self.log = config.log
        
        
        self.data_matches = [
            (self.re_PING, ircparser._ping_parser),
            (self.re_PRIVMSG, ircparser._privmsg_parser),
            (self.re_NOTICE, ircparser._notice_parser),
            (self.re_JOIN, ircparser._join_parser),
            (self.re_PART, ircparser._part_parser),
            (self.re_QUIT, ircparser._quit_parser),
            (self.re_MODE, ircparser._mode_parser),
            (self.re_NICK, ircparser._nick_parser),
            (self.re_RAW, ircparser._raw_parser),
            (self.re_ERRO, ircparser._err_parser),
        ]
        
        self.default_events = [
                            ['PRIVMSG', '(.+)!.+','.+','\x01VERSION\x01(.*)',send_version],
                            ['PRIVMSG', '(.+)!.+','.+','\x01FINGER\x01(.*)',send_finger],
                            ['PRIVMSG', '(.+)!.+','.+','\x01TIME\x01(.*)',send_time],
                            ['PRIVMSG', '(hukk)!.+','(.+)','(!reload)$', doreload],
                            ['PRIVMSG', '(hukk)!.+','(.+)','(!showinfo)$', show_info],
                        ]
        
        self.events =   []
                        
        self._load_ext_modules()
               
    def _thread(self):
        while self.connected:
            try:
                data = self._get()
                if data.strip() != "":
                    rawnicks = re.findall("[0-9]{3} %s "%self.me,data) 
                    keys = []
                    for r in rawnicks: 
                        if r not in keys:
                            keys.append(r)
                            data = data.replace(r,"\n"+r)
                    for d in data.split("\n"):
                        if d.strip() != "":
                            self._log("GET: %s"%d)                            
                            self._ircparser(d)
            except UnicodeDecodeError:
                self._log("Unicode Error: %s" % data)
    
    def _connect(self):
        self.sock.connect((self.host,self.port))
        self.connected = True
        thread.start_new_thread(self._thread,())
        
    def _load_ext_modules(self):
        for m in config.modulestoimport:
            try:
                file, pathname, description = imp.find_module(m)
                mod = imp.load_module(m,file, pathname, description)
                self.loaded_modules.append(mod)
                if mod.__doc__:
                    print "%s... Loaded!" % mod.__doc__
            except ImportError, (errstr):
                print "Error when trying to load '%s' module: %s" % (m,errstr)
                
        self._load_mod_events()
        
    def _load_mod_events(self):
        del self.events
        self.events = []
        self.events.extend(self.default_events)
        for mod in self.loaded_modules:
            self.events.extend(mod.events)
        
    def _ircparser(self,data):
        for regex, function, in self.data_matches:
            m = ereg(regex,data)
            if m: function(self,m)
        
    def _pong(self,pingmsg):
        self._raw("PONG :%s" % pingmsg)
        
    def _log(self,msg):
        if self.log:
            strmsg = str(msg).replace("\r\n","")
            open("./pyrc.log","a").write("%s - %s\n" % (time.time(),strmsg))

    def _raw(self,cmd):
        self.sock.send("%s\r\n" % cmd)
        self._log("RAW: %s" % cmd.strip())

    def _get(self):
        get = self.sock.recv(self.buf)
        return get
    
    def getchan(self,chan):
        return self.chan[self.chan.index(chan)]
    
    def connect(self):
        self._connect()
        self.setnick(self.me)
        self.setuser(self.user)
        for chan in config.channels:
            self.join(chan)
        
    def reconnect(self):
        self.quit("Booting connection...")
        self.connect()
        
    def reloadconfig(self):
        try:
            reload(config)
            for mod in self.loaded_modules:
                if mod.__doc__:
                    print "%s... Reloaded!" % mod.__doc__
                reload(mod)
            self._load_mod_events()
            print "Pyrc Reloaded!"
        except Exception, (errstr):
            print errstr
    
    def setnick(self, nick):
        self._raw("NICK %s" % nick)
        self.me = nick
    
    def setuser(self,user):
        self._raw("USER %s" % user)
        
    def msg(self, dest, msg):
        self._raw("PRIVMSG %s :%s" % (dest,msg))
    
    def notice(self, dest, msg):
        self._raw("NOTICE %s :%s" % (dest,msg))
    
    def join(self,chan):
        self._raw("JOIN %s" % chan)
        self.chan.append(Chan(chan))
        
    def part(self,chan,msg=None):
        if chan in self.chan:
            if msg:
                self._raw("PART %s :%s" % (chan,msg))
            else:
                self._raw("PART %s" % (chan))
            self.chan.remove(Chan(chan))
    
    def mode(self,chan,param):
        if (chan[0] == "#" and chan in self.chan) or \
           (chan[0] != "#" and chan != self.me):
            self._raw("MODE %s %s" % (chan,param))

    def quit(self,msg="Bye bye"):
        self._raw("QUIT :%s" % msg.strip())
        self.chan = []
        del self.sock
        self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.connected = False
